
#include "InpReader.hpp"

NAMESPACE_BEGIN(NSInpReader)

TopoDS_Shape InpReader::Read() {
    QFile file(QString::fromStdString(filename_));
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        std::cerr << "Error: Cannot open " << filename_ << std::endl;
        return TopoDS_Shape();
    }

    QTextStream in(&file);
    std::vector<gp_Pnt> nodes;
    std::vector<std::vector<int>> elements;

    QString line;
    while (in.readLineInto(&line)) {
        if (line.contains("*Node")) {
            ReadNodes(in, nodes);
        }
    }
    in.seek(0);
    while (in.readLineInto(&line)) {
        if (line.contains("*Element")) {
            ReadElements(in, elements);
        }
    }

    std::cout << elements.size() << std::endl;
    file.close();

    return CreateShapeFromData(nodes, elements);
}

void InpReader::ReadNodes(QTextStream& in, std::vector<gp_Pnt>& nodes) {
    QString line;
    while (in.readLineInto(&line)) {
        if (line.trimmed().isEmpty() || line.trimmed().startsWith('*')) break;

        QStringList parts = line.split(',');
        if (parts.size() != 4) {
            std::cerr << "Error reading node: " << line.toStdString() << std::endl;
            continue;
        }

        bool ok;
        int id = parts[0].trimmed().toInt(&ok);
        if (!ok) {
            std::cerr << "Error converting node ID: " << parts[0].toStdString() << std::endl;
            continue;
        }

        double x = parts[1].trimmed().toDouble(&ok);
        if (!ok) {
            std::cerr << "Error converting x coordinate: " << parts[1].toStdString() << std::endl;
            continue;
        }

        double y = parts[2].trimmed().toDouble(&ok);
        if (!ok) {
            std::cerr << "Error converting y coordinate: " << parts[2].toStdString() << std::endl;
            continue;
        }

        double z = parts[3].trimmed().toDouble(&ok);
        if (!ok) {
            std::cerr << "Error converting z coordinate: " << parts[3].toStdString() << std::endl;
            continue;
        }

        nodes.emplace_back(x, y, z);
        std::cout << "Node " << id << ": (" << x << ", " << y << ", " << z << ")" << std::endl;
    }
}

    void InpReader::ReadElements(QTextStream& in, std::vector<std::vector<int>>& elements) {
        QString line;
        while (in.readLineInto(&line)) {
            if (line.trimmed().isEmpty() || line.trimmed().startsWith('*')) break;

            // Remove leading and trailing spaces
            line = line.trimmed();

            // Split the line by commas
            QStringList parts = line.split(',');

            // Ensure there are exactly 9 parts (1 ID, 8 node IDs)
            if (parts.size() != 9) {
                std::cerr << "Error: Incorrect number of parts for element " << parts[0].trimmed().toStdString() << ". Expected 9, got " << parts.size() << std::endl;
                continue;
            }

            bool ok;
            int id = parts[0].trimmed().toInt(&ok);
            if (!ok) {
                std::cerr << "Error converting element ID: " << parts[0].trimmed().toStdString() << std::endl;
                continue;
            }

            std::vector<int> element;
            for (int i = 1; i < 9; ++i) { // Read 8 node IDs
                int node_id = parts[i].trimmed().toInt(&ok);
                if (!ok) {
                    std::cerr << "Error converting node ID: " << parts[i].trimmed().toStdString() << std::endl;
                    continue;
                }
                element.push_back(node_id - 1); // Adjust index to 0-based
            }

            if (element.size() != 8) {
                std::cerr << "Error: Incorrect number of nodes for element " << id << ". Expected 8, got " << element.size() << std::endl;
                continue;
            }

            elements.push_back(element);
            std::cout << "Element " << id << ": [";
            for (int node_id : element) {
                std::cout << node_id + 1 << " ";
            }
            std::cout << "]" << std::endl;
        }
    }


    TopoDS_Shape InpReader::CreateShapeFromData(const std::vector<gp_Pnt>& nodes, const std::vector<std::vector<int>>& elements) {
        BRep_Builder builder;
        TopoDS_Compound compound;
        builder.MakeCompound(compound);

        // Step 1: Create a map to store unique vertices
        std::map<int, TopoDS_Vertex> vertex_map;
        for (size_t i = 0; i < nodes.size(); ++i) {
            TopoDS_Vertex vertex;
            builder.MakeVertex(vertex, nodes[i], Precision::Confusion());
            vertex_map[i] = vertex;
        }

        for (const auto& element : elements) {

            if (element.size() == 8) { // Hexahedron element (C3D8R)
             // Create the edges
                std::vector<TopoDS_Edge> edges;
                for (int i = 0; i < 4; ++i) {
                    edges.push_back(BRepBuilderAPI_MakeEdge(vertex_map[element[i]], vertex_map[element[(i + 1) % 4]]).Edge());
                    edges.push_back(BRepBuilderAPI_MakeEdge(vertex_map[element[i + 4]], vertex_map[element[(i + 1) % 4 + 4]]).Edge());
                }
                edges.push_back(BRepBuilderAPI_MakeEdge(vertex_map[element[0]], vertex_map[element[4]]).Edge());
                edges.push_back(BRepBuilderAPI_MakeEdge(vertex_map[element[1]], vertex_map[element[5]]).Edge());
                edges.push_back(BRepBuilderAPI_MakeEdge(vertex_map[element[2]], vertex_map[element[6]]).Edge());
                edges.push_back(BRepBuilderAPI_MakeEdge(vertex_map[element[3]], vertex_map[element[7]]).Edge());

                // Create the faces
                std::vector<int> face_indices[] = {
                    {0, 1, 2, 3}, // Bottom face
                    {4, 5, 6, 7}, // Top face
                    {0, 1, 5, 4}, // Front face
                    {1, 2, 6, 5}, // Right face
                    {2, 3, 7, 6}, // Back face
                    {3, 0, 4, 7}  // Left face
                };

                std::vector<TopoDS_Face> faces;
                for (const auto& indices : face_indices) {
                    BRepBuilderAPI_MakePolygon poly;
                    for (int i : indices) {
                        poly.Add(vertex_map[element[i]]);
                    }
                    poly.Close();
                    TopoDS_Wire wire = poly.Wire();
                    TopoDS_Face face = BRepBuilderAPI_MakeFace(wire).Face();
                    faces.push_back(face);
                }

                // Create the shell and solid
                TopoDS_Shell shell;
                builder.MakeShell(shell);
                for (const auto& face : faces) {
                    builder.Add(shell, face);
                }

                TopoDS_Solid solid;
                builder.MakeSolid(solid);
                builder.Add(solid, shell);

                // Add the solid to the compound
                builder.Add(compound, solid);
            }
        }

        return compound;
    }


NAMESPACE_END